package viper.receiver;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import viper.main.StateManager;
import viper.main.ViperHeader;
import viper.sender.UDPSender;

public class UDPReceiverWorker implements Runnable {
	
	public DatagramPacket packet;
	private byte[] bytes;
	private byte[] temp;

	public UDPReceiverWorker(DatagramPacket packet)
	{
		this.packet = packet;
	}
	
	@Override
	public void run() {
		//parse packet
		bytes = packet.getData();
		
		switch(bytes[0]) {
			case ViperHeader.RELAY:
				handleRelayPacket();
				break;
			case ViperHeader.RECEIVE: 
				CallReceiver.INSTANCE.parsePacket(
						ViperHeader.getPayloadFromReceivePacket(bytes)
						); //handle receive packet
//				parsePacket(
//						ViperHeader.getPayloadFromReceivePacket(bytes)
//						);
				break;
			case ViperHeader.CALL_SETUP: 
				String sourceAddrIP = packet.getAddress().getHostAddress();
				handleCallSetupPacket(sourceAddrIP);
				break;
			default: 
				System.out.println("ignore"); 
				break;
		} 
	}
	
	private void handleCallSetupPacket(String sourceAddrIP)
	{
		switch(bytes[1]) {
			case ViperHeader.SETUP_MAKE_CALL: //somebody wants to talk
				//extract type of code and relay info
				byte[] payload = ViperHeader.getPayloadFromSetupPacket(bytes);
				byte typeCode = payload[0];
				int numRelays=0;
				if(typeCode==ViperHeader.CODE_111)
					numRelays = 1;
				else if(typeCode==ViperHeader.CODE_232)
					numRelays=3;
				else if(typeCode==ViperHeader.CODE_364)
					numRelays=6;
				String[] relays = new String[numRelays];
				for(int i=0; i<numRelays; i++)
				{
					byte[] ipBytes = new byte[]{
							payload[4*i + 1],
							payload[4*i + 2],
							payload[4*i + 3],
							payload[4*i + 4]
						};
					relays[i] = 
						UDPSender.ipBytesToInetAddress(ipBytes).getHostAddress();
				}
				StateManager.INSTANCE.gotCall(sourceAddrIP, typeCode, relays);
				break;
			case ViperHeader.SETUP_ACCEPT_CALL:
				//person accepted your call
				StateManager.INSTANCE.callAccepted();
				break;
			case ViperHeader.SETUP_REJECT_CALL:
				//person rejected your call
				StateManager.INSTANCE.callRejected();
				break;
			case ViperHeader.SETUP_ERROR:
				//handle error
				break;
			case ViperHeader.SETUP_CANCEL_CALL:
				//call cancelled
				StateManager.INSTANCE.callCancelled();
				break;
			case ViperHeader.SETUP_END_CALL:
				//call ended
				StateManager.INSTANCE.callEnded();
				break;
			default: 
				System.out.println("ignored call setup packet"); 
				break; 
		}
	}
	
	/* At a relay, upon receiving a packet from the Sender, the relay changes the VIPER header
	of the packet and forwards it to the sender.
	*/ 
	private void handleRelayPacket() {
		
		/* Format of a relay packet
		 * <Relay (1 byte)> <RX IP Addr (4 bytes)> <Seq # (1 byte)> <Pkt Type (1 byte)> <Data>
		 */
		
		//System.out.println("Relay"); // for troubleshooting
		//temp is a temporary byte[] to store the payload for the packet to be sent to the RX
		temp = new byte[bytes.length - 4];
		temp[0] = ViperHeader.RECEIVE;
		System.arraycopy(bytes, 5, temp, 1, bytes.length - 5);
		packet.setData(temp);
		byte[] ipByte = new byte[4];
		System.arraycopy(bytes, 1, ipByte, 0, 4);
		InetAddress rxAddr = null;
		
		try {
			rxAddr = InetAddress.getByAddress(ipByte);
			packet.setAddress(rxAddr);
			StateManager.INSTANCE.getDatagramSocket().send(packet);
			
		} catch (UnknownHostException e1) {
			e1.printStackTrace();
		}catch (SocketException e2) {
			e2.printStackTrace();
		} catch (IOException e3) {
			e3.printStackTrace();
		}
	}
	
//	public void parsePacket(byte[] buff) 
//	{
//		if(!CallReceiver.INSTANCE.isReadyToParse()) //player is not ready yet
//			return;
//
//		byte[] timeBytes = new byte[]{buff[0], buff[1], buff[2], buff[3], buff[4], buff[5], buff[6], buff[7]};
//		long timeStamp = ViperHeader.byteArrayToLong(timeBytes);
//		if(!CallReceiver.INSTANCE.gotFirstPacket)
//		{
//			System.out.println("should not get here more than once");
//			CallReceiver.INSTANCE.t_diff =  System.currentTimeMillis() - timeStamp;
//			//t_play = timeStamp - initial_cushion;
//			CallReceiver.INSTANCE.gotFirstPacket = true;			
////			new Timer().schedule(new ViperTimerTask(), 0);
//			new Thread(CallReceiver.INSTANCE.new ViperRunnable()).start();
//		}
//
//		//byte pktType = buff[1]; //TODO: make it 0, 1, ...
//		int pktType = 0; //for 1,0,1
//
//		byte[] data = new byte[buff.length - 9];
//		System.arraycopy(buff, 9, data, 0, data.length);
//		
////		if(timeStamp < CallReceiver.INSTANCE.t_play)
////		{
////			//System.out.println("drop packet: timestamp " + timeStamp + ", t_play " + t_play);
////			return; //drop packet
////		}
////		else
////		{
////			Long timeLong = new Long(timeStamp);
//////			if(!windowTimes.contains(timeLong))
////			{
////				System.out.println("add packet: timestamp " + timeStamp + ", t_play " + CallReceiver.INSTANCE.t_play);
////				CallReceiver.INSTANCE.addToWindowTimes(timeLong);
////				byte[][] newByteArr = new byte[1][];
////				newByteArr[0] = data;
////				CallReceiver.INSTANCE.window.put(timeLong, newByteArr);
////				System.out.println("window size: " + CallReceiver.INSTANCE.window.size());
////			}
//////			else
//////			{
//////				System.out.println("add packet to existing: " + t_play);
//////				byte barr[][] = window.get(timeLong);
//////				barr[pktType] = data;
//////			}
////		}		
//		if(timeStamp > CallReceiver.INSTANCE.t_play)
//		{
//			System.out.println("add packet: timestamp " + timeStamp + ", t_play " + CallReceiver.INSTANCE.t_play);
//			CallReceiver.INSTANCE.addToWindowTimes(new Long(timeStamp));
//			byte[][] newByteArr = new byte[1][];
//			newByteArr[0] = data;
//			CallReceiver.INSTANCE.window.put(new Long(timeStamp), new byte[][]{data});
//			System.out.println("window size: " + CallReceiver.INSTANCE.window.size());
//		}
//	}
//
//
//	/* At the receiver, when a receive packet is received, they are sorted in order in 
//	 * RCV_BUFFER if they are within a valid sliding window range.
//	 */
////	private void handleReceivePacket()
////	{
////		System.out.println("Receive"); 
////	}
		
}
